package com.leen.admin.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import com.leen.admin.dto.AdminDTO;
import com.leen.admin.dto.AdminLoginDTO;
import com.leen.admin.entity.*;
import com.leen.admin.repository.AdminRepository;
import com.leen.admin.service.AdminService;
import com.leen.admin.vo.menu.MenuVo;
import com.leen.common.vo.ResponseVO;
import com.leen.common.vo.ResultEnum;
import com.leen.common.vo.ResultVO;
import com.leen.security.TokenService;
import com.leen.security.domain.AdminLoginUserDetails;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.servlet.http.HttpServletRequest;
import java.util.*;

/**
 * 类说明
 *
 * @author admin
 * @date 2021-05-12
 */
@Lazy
@Service
@Log4j2
@Transactional(rollbackFor = Exception.class)
public class AdminServiceImpl extends CurdServiceImpl<Admin,Integer> implements AdminService {

    private AdminRepository adminRepository;

    @Autowired
    public void setAdminRepository(AdminRepository adminRepository){
        this.adminRepository=adminRepository;
        super.setCrudRepository(adminRepository);
    }

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private TokenService tokenService;

    /**
     * 对称密码私有钥匙
     */
    @Value("${rsa.private_key}")
    private String privateKey;

    @Override
    public ResultVO login(AdminLoginDTO adminLoginDTO, HttpServletRequest request) {

        //验证验证码是否正确
        if(StringUtils.isBlank(adminLoginDTO.getKey())){
            log.debug("验证码key为空的");
            return ResultVO.failed(ResultEnum.SMS_CODE_ERROR);
        }
        //redis中缓存不存在
        String redisKey= "captcha:"+adminLoginDTO.getKey();
        String redisValue=this.stringRedisTemplate.opsForValue().get(redisKey);
        //马上删除
        this.stringRedisTemplate.delete(redisKey);

        if(StringUtils.isBlank(redisValue)){
            log.debug("{}key过期了",redisKey);
            return ResultVO.failed(ResultEnum.SMS_CODE_ERROR);
        }
        //redis中的值与你输入的值不匹配
        if(!redisValue.equalsIgnoreCase(adminLoginDTO.getCode())){
            log.debug("{}key值与输入的值不匹配",redisKey);
            return ResultVO.failed(ResultEnum.SMS_CODE_ERROR);
        }

        //走spring security登录流程，
        Authentication authentication=null;

        try{

            //密码RSA先解密
            RSA rsa=new RSA(this.privateKey,null);
            String password=rsa.decryptStr(adminLoginDTO.getPassword(), KeyType.PrivateKey);
            adminLoginDTO.setPassword(password);

            //创建一个例如用户认证对象
            UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken=
                    new UsernamePasswordAuthenticationToken(adminLoginDTO.getAccount(),adminLoginDTO.getPassword());
           // 自动调用  UserDetailsServiceImpl中的loadUserByUsername方法
            authentication=this.authenticationManager.authenticate(usernamePasswordAuthenticationToken);

        }catch (AuthenticationException e){
            log.debug( "登录失败，原因： {}", e.getMessage());
            return ResultVO.failed(ResultEnum.SYSTEM_OPERATION_ERROR);
        }

        //登录成功，得到自定义类 AdminLoginUser 把它用jwt生成令牌，存入到redis，再向前端返回
        AdminLoginUserDetails  adminLoginUser= (AdminLoginUserDetails) authentication.getPrincipal();

        //生成jwt
        String token=this.tokenService.createToken(adminLoginUser);

        Map<String, Object> authInfo = new HashMap<String, Object>(2) {{
            put("token", TokenService.TOKEN_PREFIX+token);
            put("user", adminLoginUser);
        }};

        return ResultVO.success(authInfo);

    }

    @Override
    public AdminLoginUserDetails findByAccount(String account) {
        Admin  dbAdmin= this.adminRepository.findByLoginNameOrMobile(account,account);

        if(dbAdmin==null){
            return null;
        }

        //拥有权限
        Set<SimpleGrantedAuthority> authorities=new HashSet<>();

        //拥有角色
        if(dbAdmin.getAdminRoleList()!=null && dbAdmin.getAdminRoleList().size()>0){
            //循环角色
            for(AdminRole  ar :dbAdmin.getAdminRoleList()){
                Role role=ar.getRole();
                //添加角色
                SimpleGrantedAuthority roleSimple=new SimpleGrantedAuthority("ROLE_"+role.getRoleKey());
                authorities.add(roleSimple);

                //角色是否拥有某些菜单权限
                if(role.getRoleMenuList()!=null && role.getRoleMenuList().size()>0){
                    for(RoleMenu  rm : role.getRoleMenuList()){
                        Menu m=rm.getMenu();
                        //添加权限
                        if(StringUtils.isNotBlank(m.getPerms())) {
                            SimpleGrantedAuthority menuSimple = new SimpleGrantedAuthority( m.getPerms());
                            authorities.add(menuSimple);
                        }
                    }
                }
            }
        }



        AdminLoginUserDetails  adminLoginUserDetails=new AdminLoginUserDetails(dbAdmin,authorities);
        return adminLoginUserDetails;

    }

    @Override
    public ResponseVO serach(AdminDTO adminDTO) {
        //分页页码
        Pageable pageable = PageRequest.of(adminDTO.getPageNumber() - 1, adminDTO.getPageSize());
        //构建查询对象
        Specification<Admin> specification = new Specification<Admin>() {
            @Override
            public Predicate toPredicate(Root<Admin> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                List<Predicate> arr = new ArrayList<>();
                if (org.apache.commons.lang3.StringUtils.isNotBlank(adminDTO.getName())) {
                    arr.add(criteriaBuilder.like(root.get("name"), "%" + adminDTO.getName() + "%"));
                }
                if(adminDTO.getStartTime()!=null && adminDTO.getStartTime().equals("")){
                    arr.add(criteriaBuilder.greaterThanOrEqualTo(root.get("createTime"),adminDTO.getStartTime()));
                }
                if(adminDTO.getEndTime()!=null && adminDTO.getEndTime().equals("")){
                    arr.add(criteriaBuilder.greaterThanOrEqualTo(root.get("createTime"),adminDTO.getEndTime()));
                }
                if(adminDTO.getStatus()!=null && adminDTO.getStartTime().equals("")){
                    arr.add(criteriaBuilder.equal(root.get("status"),adminDTO.getStatus()));
                }
                return criteriaBuilder.and(arr.toArray(new Predicate[arr.size()]));
            }
        };
        Page<Admin> page = this.adminRepository.findAll(specification, pageable);
        return new ResponseVO(page.getTotalElements(), page.getContent());
    }


}
